package com.xmtlxx.dh.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.xmtlxx.dh.constant.DhServiceUrl;
import com.xmtlxx.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 大华服务
 *
 * @author hergreen
 * @date 2024/09/19 18:10
 **/
@Service
@Slf4j
@RequiredArgsConstructor
public class DhService {

    private final ISysConfigService sysConfigService;

    private static final String USER_NAME = "system";

    private static final String PASSWORD = "Dahua@123";

    private static final String CLIENT_TYPE = "winpc";

    private static final String CHANNEL_CODE = "10931069087582400";

    private static final String DATE_PATTERN = "yyyyMMdd'T'HHmmss'Z'";


    /**
     * 获取录像记录
     *
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @return 记录
     */
    public JSONObject getVideoRecord(String beginDateStr, String endDateStr, Integer pageNum, Integer pageSize) {

        //get token
        String token = this.getToken();

        Date beginDate = DateUtil.parse(beginDateStr);
        Date endDate = DateUtil.parse(endDateStr);
        /**
         * 请求参数
         * {
         *     "startTime": "20250126T160000Z",	开始时间
         *     "endTime": "20250128T160000Z ",	结束时间
         *     "type": 1,	用户类型 1-普通警员 2-管理员	TRUE
         *     "userName": "",	用户名称
         *     "deptCode": "",	部门编码
         *     "caseName": "",	事件名称
         *     "sign": "",	证据标记（1-已标记 0-未标记）
         *     "deviceName": "",	设备名称
         *     "fileState": 3,	文件下载状态：0-未知 1-未下载 2-下载中 3-下载完成 4-下载失败 5-已过期 6-已删除
         *     "fullPath": "",	部门全路径
         *     "page": 1,	页码	TRUE
         *     "pageSize": 15,	分页大小	TRUE
         *     "fileFormat": 1	文件类型 0-未知 1-视频 2-音频 3-图片
         *}
         */
        Map<String, Object> recordMap = new HashMap<>();
        recordMap.put("type", 2);
        recordMap.put("fileFormat", 1);
        recordMap.put("page", pageNum);
        recordMap.put("pageSize", pageSize);
        recordMap.put("startTime", DateUtil.format(beginDate, DATE_PATTERN));
        recordMap.put("endTime", DateUtil.format(endDate, DATE_PATTERN));

        /**
         * 响应内容
         * {
         *     "totalCount": 28,	返回值
         *     "nextPage": 2,
         *     "results": [
         *         {
         *             "id": 43469,	id
         *             "fileSn": "5528F750B2A755EB14FA643BC886D62D",	文件唯一标识
         *             "deviceCode": "11479745283492032",	设备编码
         *             "deviceSn": "1",	设备序列号
         *             "fileState": 3,	文件下载状态：0-未知 1-未下载 2-下载中 3-下载完成 4-下载失败 5-已过期 6-已删除
         *             "planId": 6,	关联的补录任务标识
         *             "fileName": "17.30.58-17.38.05[R][0@0][0].dav",	文件名称
         *             "fileType": 1,	文件具体类型 0-未知 1-dva 2-mp4 3-aac 4-mp3 5-jpg
         *             "filePath": "http://10.56.9.110:38048/fileStorage/file/read?fileUrl=51e998653673d87971059b40d90a8383/life_2/20250113/1/6ef83d3f-d192-11ef-aea0-0894ef9749d4.dav&thirdAccessToken=UmVzb3VjZTo2ZWY4M2QzZi1kMTkyLTExZWYtYWVhMC0wODk0ZWY5NzQ5ZDQuZGF2VXNlcklEOjE=",	文件路径
         *             "fileFormat": "1",	文件格式 0-未知 1-视频 2-音频 3-图片
         *             "fileLength": 171036806,	文件长度
         *             "createTime": "20250113T093911Z",	入库时间
         *             "startTime": "20250113T093058Z",	文件开始时间
         *             "endTime": "20250113T093805Z",	文件结束时间
         *             "fileLengthUpload": 171036806,	文件已下载大小
         *             "deviceName": "单兵320",	设备名称
         *             "sign": "0",	证据标记 1-已标记 0-未标记
         *             "evaluate": "2",	证据评价
         *             "caseNameList": [	事件名称列表
         *                 "事件_20240903164258"
         *             ],
         *             "caseCodeList": [	事件编码列表
         *                 "20240903164258"
         *             ]
         *         }
         *     ]
         * }
         */
        String videoResult = HttpRequest.post(this.getServerUrl() + DhServiceUrl.FILE_RECORD)
                .header("X-Subject-Token", token).body(JSONUtil.toJsonStr(recordMap)).execute().body();
        log.info("请求大华服务 \n地址:{} \n参数:{} \n结果:{}", this.getServerUrl() + DhServiceUrl.FILE_RECORD, recordMap, videoResult);

        String replaceUrl = this.getReplaceUrl();

        if (StrUtil.isNotBlank(replaceUrl)) {
            //匹配返回结果的url前缀
            String regex = "https?://[^/?#]+";
            videoResult = ReUtil.replaceAll(videoResult, regex, replaceUrl);
        }

        //un authorize
        this.unAuthorize(token);

        return JSONUtil.parseObj(videoResult);
    }

    /**
     * 获取录像url
     *
     * @param file      文件
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @return cn.hutool.json.JSONObject
     * @author hergreen
     * @date 2024/10/28 11:06
     */
    @Deprecated
    public JSONObject getPlaybackUrl(String file, String beginTime, String endTime) {

        //get token
        String token = this.getToken();

        /**
         * 请求参数
         *  参数     必填 类型   说明
         * fileUrl 是 证据列表查询返回的文件路径 filePath 字段
         */
        Map<String, Object> recordMap = new HashMap<>();
        recordMap.put("channelCode", CHANNEL_CODE);
        recordMap.put("location", "cloud");
        recordMap.put("scheme", "HLS");
        recordMap.put("resource", file);
        recordMap.put("locationEnable", false);
        recordMap.put("beginTime", beginTime);
        recordMap.put("endTime", endTime);

        String videoResult = HttpRequest.get(this.getServerUrl() + DhServiceUrl.PLAY_BACK_URI)
                .header("X-Subject-Token", token).form(recordMap).execute().body();
        log.info("请求大华服务 \n地址:{} \n参数:{} \n结果:{}", this.getServerUrl() + DhServiceUrl.PLAY_BACK_URI, recordMap, videoResult);

        String replaceUrl = this.getReplaceUrl();

        if (StrUtil.isNotBlank(replaceUrl)) {
            //匹配返回结果的url前缀
            String regex = "https?://[^/?#]+";
            videoResult = ReUtil.replaceAll(videoResult, regex, replaceUrl);
        }

        //un authorize
        this.unAuthorize(token);

        return JSONUtil.parseObj(videoResult);
    }


    private String getToken() {

        Digester md5 = new Digester(DigestAlgorithm.MD5);

        /**
         * 请求内容参数
         * userName：类型string。必填。用户名称。*
         * clientType：内部字段类型string。默认'web'，选填。客户端登录类型。
         * 客户端类型 描述
         * winpc 客户端
         * web 网页端
         * mac 苹果MAC端
         * android 安卓手机端
         * iphone 苹果手机端
         * pad 平台端
         * config 管理端
         * other 其他
         */
        Map<String, Object> body = new HashMap<>();
        body.put("userName", USER_NAME);
        body.put("clientType", CLIENT_TYPE);

        /**
         * • 响应内容
         *   {
         *      "realm" : "TheNextService",
         *      "randomKey" : "54b53072540eeeb8f8e9343e71f28176_d0e2bdad-1777-446b",
         *      "encryptType" : "MD5",
         *      "method" : "simple"
         *  }
         * • 响应内容参数
         * realm：类型string。服务域信息，加密过程使用。encryptType为RSA时，该值为加密公钥
         * randomKey：类型string。随机密钥种子。
         * encryptType：类型string。加密算法类型。
         * method：类型string。使用哪种加密流程。没有该字段，按通用加密流程计算，"simple"表示 按简易加密流程计算。
         */
        String firstResult = HttpUtil.post(this.getServerUrl() + DhServiceUrl.AUTHORIZE, JSONUtil.toJsonStr(body));
        log.info("请求大华服务 \n地址:{} \n参数:{} \n结果:{}", this.getServerUrl() + DhServiceUrl.AUTHORIZE, body, firstResult);
        JSONObject firstResultJson = JSONUtil.parseObj(firstResult);

        //encrypt
        String realm = firstResultJson.getStr("realm");
        String randomKey = firstResultJson.getStr("randomKey");
        String encryptType = firstResultJson.getStr("encryptType");

        String password1 = md5.digestHex(PASSWORD);
        String password2 = md5.digestHex(USER_NAME + password1);
        String passwordTmp = md5.digestHex(password2);
        String encryptedPassword = md5.digestHex(USER_NAME + ":" + realm + ":" + passwordTmp);
        String signature = md5.digestHex(encryptedPassword + ":" + randomKey);

        /**
         * • 请求内容参数
         * userName：类型string。必填。用户名称。需要和第一次交互参数一致。
         * signature：类型string。必填。根据签名计算方法得到的签名值。详细见加密流程说明。encryptType为RSA时，该值为RSA公钥加密后的结果串
         * randomKey：类型string。必填。随机密钥种子。需要和第一次交互成功返回的随机值一致。
         * encryptType：类型string。必填。加密算法。需要和第一次交互成功返回的随机值一致。
         * clientType：类型string。必填。请求方登录类型。需要和第一次交互参数一致。
         * macAddress：内部字段类型string。选填。请求方的MAC地址。需要和第一次交互参数一致，用于日志审计。
         * pid：内部字段类型int。选填。请求方的服务进程号。需要和第一次交互参数一致。
         * bit：内部字段类型int。选填。当前版本只有适用于客户端的请求方登录类型，表示客户端是32位还是64位的程序，服务端根据该值，查询对应详细位数的客户端最新信息。
         * expiredTime：内部字段类型int。选填。表示自定义超时时间，非必填字段，系统默认为120，最小120，最大86400。
         */
        body.put("signature", signature);
        body.put("randomKey", randomKey);
        body.put("encryptType", encryptType);

        /**
         * • 响应内容
         * {
         *      "duration" : 120,
         *      "token" :"S4NbecfYB19QUJHT4T8M7G_Y5524jUq1yNQtuFruA87hAErnAHoQTEXtUn1AEZz0TKwKi0EIriyrT8ul2ejSGcy7lxWndsP4+GWch2C/fcgyYAz2GJnbQtRFFbq3XNfi7iAXeEG6O40GKODI2ICTq",
         *      "userName": "system",
         *      "userId" : "S4NbecfYB19QUJHT4T8M7G",
         *      "userCode" : "S4NbecfYB19QUJHT4T8M7G",
         *      "serviceAbilty" : ["video", "synopsis"],
         *      "lastLoginIp": "127.0.0.1",
         *      "lastLoginTime": "20151119T161900Z",
         *      "versionInfo":{
         *          "lastVersion":"945960",
         *          "updateUrl" :"/data/VSL/DSSEnterpriseClient/DSS_Enterprise_Client.zip",
         *           "pluginInfo": [{
         *              "product" : "9100",
         *              "version" : "123",
         *              "url" : "/data/VSL/DSSEnterpriseClient/Plugins/9100/9100.zip",
         *              "changelog" : "/data/VSL/DSSEnterpriseClient/Plugins/9100/changelog.txt"
         *              }]
         *      }
         * }
         * • 响应内容参数
         * duration：类型int。有效时间，单位秒，对应expiredTime传参，不传时返回默认值，建议3/4duration时进行会话更新。
         * token：类型string。返回登陆令牌，令牌字符串由服务端发布。之后的其它请求，在HTTP头的X-Subject-Token: 带上这个令牌进行鉴权。
         * 注意 : 请求方获取token后，禁止使用自定义的方式来截取该token字符串以得到某些信息（如用户编码），需要得到业务信息请通过调用接口方式获取，以便后续vsl改造。token只能用作头部传值或请求传参。
         * userName：类型string。创建该会话的用户名。
         * userCode：类型string。用户的系统编码。
         * userId：类型string。同userCode字段，当前版本兼容保留，后续版本去除，请使用userCode。
         * serviceAbilty：类型array[string]。系统产品所包含的能力类型。当前版本为比较粗略的能力类型定义，主要用于客户端界面上的显示，后续根据业务需求可进行扩展。
         * 能力类型范围：
         * 能力类型 描述
         * video 视频
         * synopsis 摘要
         * vehicle 车辆
         * face 人脸
         * lastLoginIp：类型string。上次登陆的IP地址。
         * lastLoginTime：类型string。上次登陆的时间。
         * versionInfo：内部字段类型object。客户端程序版本信息，只有 clientType='winpc'时返回。
         * lastVersion：类型string。客户端最新版本号。
         * updateUrl：类型string。客户端程序更新路径。
         * pluginInfo：类型array[object]。客户端补丁程序信息。
         */
        String sendResult = HttpUtil.post(this.getServerUrl() + DhServiceUrl.AUTHORIZE, JSONUtil.toJsonStr(body));
        log.info("请求大华服务 \n地址:{} \n参数:{} \n结果:{}", this.getServerUrl() + DhServiceUrl.AUTHORIZE, body, sendResult);
        JSONObject sendResultJson = JSONUtil.parseObj(sendResult);
        return sendResultJson.getStr("token");
    }

    private void unAuthorize(String token) {

        /**
         *  请求内容参数
         *  token：类型string。必填。要注销的身份令牌token值。
         */
        HashMap<Object, Object> body = new HashMap<>();
        body.put("token", token);
        /**
         * 响应内容 {}
         * 响应内容参数
         *  空对象
         */
        String sendResult = HttpRequest.post(this.getServerUrl() + DhServiceUrl.UN_AUTHORIZE)
                .body(JSONUtil.toJsonStr(body))
                .header("X-Subject-Token", token).execute().body();
        log.info("请求大华服务 \n地址:{} \n参数:{} \n结果:{}", this.getServerUrl() + DhServiceUrl.UN_AUTHORIZE, body, sendResult);
    }


    private String getServerUrl() {

        return sysConfigService.selectConfigByKey("dh.server.url");
    }

    private String getReplaceUrl() {

        return sysConfigService.selectConfigByKey("dh.url.replace");
    }

}
